package com.kirk.cts.ui.style;

import org.apache.log4j.Logger;

import javax.swing.*;
import javax.swing.plaf.FontUIResource;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Locale;


/**
 * GUI related utility methods.
 */
public class GuiUtils {

    private static Logger logger = Logger.getLogger(GuiUtils.class);

    /**
     * The lookAndFeelColorRegister.
     */
    private static HashMap<String, Color> lookAndFeelColorRegister = new HashMap<String, Color>();

    /**
     * The component orientation.
     * 组件方向。排序
     */
    private static ComponentOrientation componentOrientation;

    /**
     * The set window shape method.
     * 设置窗口形状方法。
     */
    private static Method setWindowShapeMethod;

    /**
     * The set window opacity method.
     * 设置窗口不透明度方法。
     */
    private static Method setWindowOpacityMethod;

    /**
     * The set window opaque method.
     * 设置窗口不透明方法
     */
    private static Method setWindowOpaqueMethod;

    /**
     * Bounds of the main screen device, used to calculate sizes
     * 主屏幕设备的边界，用于计算大小
     */
    private static Rectangle mainDeviceBounds;

    private static final int MAX_COMPONENTS_WIDTH = 1280;

    public static final Color errorMessageColor = Color.RED;

    public static final Color AlertMessageColor = Color.RED;

    static {
        try {
            Class<?> awtUtilities = Class.forName("com.sun.awt.AWTUtilities");
            setWindowShapeMethod = awtUtilities.getDeclaredMethod("setWindowShape", Window.class, Shape.class);
            setWindowOpacityMethod = awtUtilities.getDeclaredMethod("setWindowOpacity", Window.class, float.class);
            setWindowOpaqueMethod = awtUtilities.getDeclaredMethod("setWindowOpaque", Window.class, boolean.class);
        } catch (ClassNotFoundException e) {
            logger.info("class com.sun.awt.AWTUtilities not found");
        } catch (SecurityException | NoSuchMethodException e) {
            logger.error(e.getMessage(), e);
        }

        mainDeviceBounds = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration().getBounds();
    }

    /**
     * Returns the width in pixels of the main device
     *
     * @return 返回主设备的宽度（以像素为单位）
     */
    public static int getDeviceWidth() {
        return mainDeviceBounds.width;
    }

    /**
     * Returns the height in pixels of the main device
     *
     * @return 返回主设备的高度（以像素为单位）
     */
    public static int getDeviceHeight() {
        return mainDeviceBounds.height;
    }

    /**
     * Sets location of a window centered in main screen device. Window must
     * have size different of (0,0)
     *
     * @param window 居中
     */
    public static void setLocation(Window window) {
        window.setLocation(mainDeviceBounds.width / 2 - window.getWidth() / 2, mainDeviceBounds.height / 2 - window.getHeight() / 2);
    }

    /**
     * Adds the close action with escape key.
     *
     * @param dialog the dialog esc 关闭窗口
     */
    public static void addCloseActionWithEscapeKey(final JDialog dialog) {
        //  Handle escape key to close the dialog

        KeyStroke escape = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, false);
        Action escapeAction = new AbstractAction() {
            private static final long serialVersionUID = 0L;

            @Override
            public void actionPerformed(ActionEvent e) {
                dialog.setVisible(false);
            }
        };
        dialog.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(escape, "ESCAPE");
        dialog.getRootPane().getActionMap().put("ESCAPE", escapeAction);
    }

    /**
     * Adds the close action with escape key.
     *
     * @param frame the frame
     */
    public static void addCloseActionWithEscapeKey(final JFrame frame) {
        //  Handle escape key to close the dialog

        KeyStroke escape = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, false);
        Action escapeAction = new AbstractAction() {
            private static final long serialVersionUID = 0L;

            @Override
            public void actionPerformed(ActionEvent e) {
                frame.setVisible(false);
            }
        };
        frame.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(escape, "ESCAPE");
        frame.getRootPane().getActionMap().put("ESCAPE", escapeAction);
    }

    /**
     * Adds the dispose action with escape key.
     *
     * @param dialog the dialog
     */
    public static void addDisposeActionWithEscapeKey(final JDialog dialog) {
        //  Handle escape key to close the dialog

        KeyStroke escape = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, false);
        Action disposeAction = new AbstractAction() {
            private static final long serialVersionUID = 0L;

            @Override
            public void actionPerformed(ActionEvent e) {
                dialog.dispose();
            }
        };
        dialog.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(escape, "ESCAPE");
        dialog.getRootPane().getActionMap().put("ESCAPE", disposeAction);
    }

    /**
     * Adds the dispose action with escape key.
     *
     * @param frame the frame
     */
    public static void addDisposeActionWithEscapeKey(final JFrame frame) {
        //  Handle escape key to close the dialog

        KeyStroke escape = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, false);
        Action disposeAction = new AbstractAction() {
            private static final long serialVersionUID = 0L;

            @Override
            public void actionPerformed(ActionEvent e) {
                frame.dispose();
            }
        };
        frame.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(escape, "ESCAPE");
        frame.getRootPane().getActionMap().put("ESCAPE", disposeAction);
    }

    /**
     * Applies Locale specific component orientation to containers.
     *
     * @param containers One or more containers
     */
    public static void applyComponentOrientation(Container... containers) {
        if (componentOrientation == null) {
            setComponentOrientation();
        }
        for (Container container : containers) {
            container.applyComponentOrientation(componentOrientation);
        }
    }

    /**
     * Collapses all nodes in a tree.
     *
     * @param tree the tree
     */
    public static void collapseTree(JTree tree) {
        for (int i = tree.getRowCount() - 1; i > 0; i--) {
            tree.collapseRow(i);
        }
        tree.setSelectionRow(0);
    }

    /**
     * Expands all nodes in a tree.
     *
     * @param tree A tree
     */
    public static void expandTree(JTree tree) {
        for (int i = 1; i < tree.getRowCount(); i++) {
            tree.expandRow(i);
        }
        tree.setSelectionRow(0);
    }

    /**
     * Returns background color for panels, as set by Look And Feel.
     *
     * @return the background color
     */
    public static Color getBackgroundColor() {
        return (Color) UIManager.get("Panel.background");
    }

    /**
     * Returns foreground color for labels, as set by Look And Feel
     *
     * @return the forefround color
     */
    public static Color getForegroundColor() {
        return (Color) UIManager.get("Label.foreground");
    }


    /**
     * get lookAndFeelColor by color name.
     */
    public static Color getLookAndFeelColor(String colorName) {
        return (Color) lookAndFeelColorRegister.get(colorName != null ? colorName.trim() : " ");
    }

    /**
     * store lookAndFeelColor.
     */
    public static void putLookAndFeelColor(String colorName, Color c) {
        lookAndFeelColorRegister.put(colorName, c);
    }

    /**
     * Returns the component orientation.
     *
     * @return The component orientation
     */
    public static ComponentOrientation getComponentOrientation() {
        if (componentOrientation == null) {
            setComponentOrientation();
        }
        return componentOrientation;
    }

    /**
     * Returns the component orientation as a SwingConstant.
     *
     * @return The component orientation as a SwingConstant
     */
    public static int getComponentOrientationAsSwingConstant() {
        if (componentOrientation == null) {
            setComponentOrientation();
        }
        return componentOrientation.isLeftToRight() ? SwingConstants.LEFT : SwingConstants.RIGHT;
    }

    /**
     * Returns a proportional width according to screenWidth and desiredSize for
     * the current screen resolution.
     *
     * @param screenWidth  the screen width
     * @param desiredWidth the desired width
     * @return the component width for resolution
     */
    public static int getComponentWidthForResolution(int screenWidth, int desiredWidth) {
        int currentScreenWidth = mainDeviceBounds.width > MAX_COMPONENTS_WIDTH ? MAX_COMPONENTS_WIDTH : mainDeviceBounds.width;
        int result = desiredWidth * currentScreenWidth / screenWidth;
        return result;
    }

    /**
     * Returns a proportional height according to screenHeight and desiredHeight
     * for the current screen resolution.
     *
     * @param screenHeight  the screen height
     * @param desiredHeight the desired height
     * @return the component height for resolution
     */
    public static int getComponentHeightForResolution(int screenHeight, int desiredHeight) {
        int currentScreenHeight = mainDeviceBounds.height;
        int result = desiredHeight * currentScreenHeight / screenHeight;
        return result;
    }


    /**
     * Sets the component orientation.
     */
    private static void setComponentOrientation() {
        componentOrientation = ComponentOrientation.getOrientation(Locale.getDefault());
    }

    /**
     * Sets the default font for all Swing components.
     *
     * @param f the f
     */
    public static void setUIFont(FontUIResource f) {
        Enumeration<Object> keys = UIManager.getDefaults().keys();
        while (keys.hasMoreElements()) {
            Object key = keys.nextElement();
            Object value = UIManager.get(key);
            if (value instanceof FontUIResource) {
                UIManager.put(key, f);
            }
        }
    }

    /**
     * Sets the window shape if possible.
     *
     * @param window A mindow
     * @param mask   A mask
     */
    public static void setWindowShape(Window window, Shape mask) {
        if (setWindowShapeMethod != null) {
            try {
                setWindowShapeMethod.invoke(null, window, mask);
            } catch (SecurityException | IllegalArgumentException |
                    IllegalAccessException | InvocationTargetException |
                    UnsupportedOperationException e) {
                logger.error(e.getMessage(), e);
            }
        }
    }

    /**
     * Sets the window opacity if possible.
     *
     * @param window  A window
     * @param opacity Opacity from 0 to 1
     */

    public static void setWindowOpacity(Window window, float opacity) {
        if (setWindowOpacityMethod != null) {
            try {
                setWindowOpacityMethod.invoke(null, window, opacity);
            } catch (SecurityException | IllegalAccessException | IllegalArgumentException
                    | UnsupportedOperationException e) {
                logger.error(e.getMessage(), e);
            } catch (InvocationTargetException e) {
                logger.error(e.getMessage(), e);
                setWindowOpacityMethod = null;
            }
        }
    }

    /**
     * Sets the window opaque if possible.
     *
     * @param window A window
     * @param opaque If the window should be opaque
     */

    public static void setWindowOpaque(Window window, boolean opaque) {
        if (setWindowOpaqueMethod != null) {
            try {
                setWindowOpaqueMethod.invoke(null, window, opaque);
            } catch (SecurityException | IllegalArgumentException |
                    IllegalAccessException | UnsupportedOperationException e) {
                logger.error(e.getMessage(), e);
            } catch (InvocationTargetException e) {
                logger.error(e.getMessage(), e);
                setWindowOpaqueMethod = null;
            }
        }
    }

}
